#include "netclient.h"

NetClient::NetClient(endPointIteratorType endpoint_iterator)
    : m_socket(NetService::IOService().getIOService())
{
    boost::asio::async_connect(m_socket, endpoint_iterator,
        boost::bind(&NetClient::handle_connect, this,
            boost::asio::placeholders::error));
}

void NetClient::write(const NetChatMsg& a_msg)
{
    NetService::IOService().getIOService().post(boost::bind(&NetClient::do_write, this, a_msg));
}

void NetClient::close()
{
    NetService::IOService().getIOService().post(boost::bind(&NetClient::do_close, this));
}

void NetClient::handle_connect(const boost::system::error_code& a_error)
{
  if (!a_error)
  {
    boost::asio::async_read(m_socket,
        boost::asio::buffer(m_read_msg.data(), NetChatMsg::header_length),
           boost::bind(&NetClient::handle_read_header, this,
               boost::asio::placeholders::error));
  }
}

void NetClient::handle_read_header(const boost::system::error_code& error)
{
   if (!error && m_read_msg.decode_header())
   {
       boost::asio::async_read(m_socket,
           boost::asio::buffer(m_read_msg.body(), m_read_msg.body_length()),
               boost::bind(&NetClient::handle_read_body, this,
                   boost::asio::placeholders::error));
   }
   else
   {
       do_close();
   }
}

void NetClient::handle_read_body(const boost::system::error_code& error)
{
   if (!error)
   {
       std::cout.write(m_read_msg.body(), m_read_msg.body_length());
       std::cout << "\n";
       boost::asio::async_read(m_socket,
           boost::asio::buffer(m_read_msg.data(), NetChatMsg::header_length),
               boost::bind(&NetClient::handle_read_header, this,
                   boost::asio::placeholders::error));
   }
   else
   {
       do_close();
   }
}

void NetClient::do_write(NetChatMsg msg)
{
   bool write_in_progress = !m_write_msgs.empty();
   m_write_msgs.push_back(msg);
   if (!write_in_progress)
   {
       boost::asio::async_write(m_socket,
           boost::asio::buffer(m_write_msgs.front().data(),
               m_write_msgs.front().length()),
                   boost::bind(&NetClient::handle_write, this,
                       boost::asio::placeholders::error));
   }
}

void NetClient::handle_write(const boost::system::error_code& error)
{
   if (!error)
   {
       m_write_msgs.pop_front();
       if (!m_write_msgs.empty())
       {
           boost::asio::async_write(m_socket,
               boost::asio::buffer(m_write_msgs.front().data(),
                   m_write_msgs.front().length()),
                       boost::bind(&NetClient::handle_write, this,
                           boost::asio::placeholders::error));
       }
   }
   else
   {
       do_close();
   }
}

void NetClient::do_close()
{
    m_socket.close();
}
